#ifndef _CHILD_PROCESS_H_
#define _CHILD_PROCESS_H_

#include <utility>
#include <thread>
#include <functional>

// need subprocess.h, from: https://github.com/sheredom/subprocess.h
#include <subprocess.h>

class ChildProcess
{
public:
    using proc_out_handler  = std::function<void(void* param, uint8_t* , uint32_t)>;
    using proc_exit_handler = std::function<void(void* param)>;

private:
    volatile bool       is_quit_;
    bool                is_create_param_;
    struct subprocess_s sub_proc_;
    proc_out_handler    stdout_handler_;
    proc_out_handler    stderr_handler_;
    proc_exit_handler   exit_handler_;
    std::thread         stdout_thread_;
    std::thread         stderr_thread_;
    std::thread         exit_thread_;

    void*               create_param_;

    // 循环读取并写到回调中
    void _exit_thread_func(pid_t id, proc_exit_handler& handle) {
        if (0 == id) {
            printf("_exit_thread_func, invalid id\n");
            return;
        }
        int status;
        while(1) {
            // 线程退出
            if (is_quit_) {
                break;
            }
            if (id != waitpid(id, &status, 0)) {
                return;
            }
            handle(create_param_);
            break;
        }
    };

    // 循环读取并写到回调中
    void _read_and_out_thread_func(FILE* file_, proc_out_handler& handle) {
        if (nullptr == file_) {
            printf("_read_and_out_thread_func, invalid file handle\n");
            return;
        }
        uint32_t out_len;
        uint8_t out_buf[1024];
        while(1) {
            // 线程退出
            if (is_quit_) {
                break;
            }
            // 读取文件
            memset(out_buf, 0, sizeof(out_buf));
            out_len = fread(out_buf, 1, sizeof(out_buf), file_);
            // 线程退出
            if (is_quit_) {
                break;
            }
            // 如果有内容，则输出
            if (out_len > 0 && out_len <= sizeof(out_buf)) {
                handle(create_param_, out_buf, out_len);
            }
            // 如果为0，则是出错退出了。
            if (out_len == 0) {
                break;
            }
        }
    };

public:
    explicit ChildProcess(): is_quit_(false), is_create_param_(false), create_param_(nullptr) {
        memset(&sub_proc_, 0, sizeof(sub_proc_));
    };
    //添加移动构造函数
    ChildProcess(ChildProcess &&d){
        is_quit_              = d.is_quit_;
        is_create_param_      = d.is_create_param_;
        sub_proc_             = d.sub_proc_;

        stdout_handler_       = d.stdout_handler_;
        stderr_handler_       = d.stderr_handler_;
        exit_handler_         = d.exit_handler_;

        stdout_thread_        = std::move(d.stdout_thread_);
        stderr_thread_        = std::move(d.stderr_thread_);
        exit_thread_          = std::move(d.exit_thread_);

        create_param_         = d.create_param_;

        d.create_param_       = nullptr;
        d.is_create_param_    = false;
        d.is_quit_            = false;
        memset(&d.sub_proc_, 0, sizeof(d.sub_proc_));
    };
    ~ChildProcess() {
        DestoryProcess();
    };
    void DestoryProcess(){
        // 子线程需要准备退出了
        is_quit_ = true;
        if (!sub_proc_.alive) {
            return;
        }
        // 先终止线程
        subprocess_terminate(&sub_proc_);
        // 再销毁句柄
        subprocess_destroy(&sub_proc_);
        // 清除数据
        memset(&sub_proc_, 0, sizeof(sub_proc_));
        // 线程join
        if (exit_thread_.joinable()) {
            exit_thread_.join();
        }
        if (stderr_thread_.joinable()) {
            stderr_thread_.join();
        }
        if (stdout_thread_.joinable()) {
            stdout_thread_.join();
        }
        // 释放内存
        if (is_create_param_ && (nullptr != create_param_)) {
            free(create_param_);
        }
        is_create_param_ = false;
        create_param_ = nullptr;
    };

    bool CreateProcess(const char *const commandLine[], int options, void* param, uint32_t param_size = sizeof(void*)) {
        // 不允许重复创建
        if (sub_proc_.alive) {
            printf("dup CreateProcess.\n");
            return false;
        }
        // 先拷贝外部参数
        void* create_param_ = param;
        if (param_size > sizeof(void*)) {
            create_param_ = malloc(param_size);
            if (nullptr == create_param_) {
                printf("CreateProcess, alloc memory fail.\n");
                return false;
            }
            memcpy(create_param_, param, param_size);
            is_create_param_ = true;
        }

        // 创建进程
        if (0 != subprocess_create(commandLine, options, &sub_proc_)) {
            memset(&sub_proc_, 0, sizeof(sub_proc_));
            // 释放内存
            if (is_create_param_ && (nullptr != create_param_)) {
                free(create_param_);
            }
            is_create_param_ = false;
            create_param_ = nullptr;
            printf("subprocess_create fail.\n");
            return false;
        }

        // 创建两个监听输出线程、一个线程退出线程
        is_quit_ = false;
        stdout_thread_ = std::move(std::thread(std::bind(&ChildProcess::_read_and_out_thread_func, this, sub_proc_.stdout_file, stdout_handler_)));
        stderr_thread_ = std::move(std::thread(std::bind(&ChildProcess::_read_and_out_thread_func, this, sub_proc_.stderr_file, stderr_handler_)));
        exit_thread_   = std::move(std::thread(std::bind(&ChildProcess::_exit_thread_func, this, sub_proc_.child, exit_handler_)));

        return true;
    };
    pid_t GetChildId() {
        return sub_proc_.child;
    };
    bool WriteToChild(const char* buf, uint32_t buf_len) {
        if (0 == sub_proc_.alive) {
            return false;
        }
        if (nullptr == sub_proc_.stdin_file) {
            return false;
        }
        fwrite(buf, buf_len, 1, sub_proc_.stdin_file);
        return true;
    };
    inline ChildProcess& ChildExit(proc_exit_handler handler) {
        exit_handler_ = std::move(handler);
        return *this;
    };
    inline ChildProcess& StdOut(proc_out_handler handler) {
        stdout_handler_ = std::move(handler);
        return *this;
    };
    inline ChildProcess& StdErr(proc_out_handler handler) {
        stderr_handler_ = std::move(handler);
        return *this;
    };
};

#endif
